home *** CD-ROM | disk | FTP | other *** search
/ Total Network Tools 2002 / NextStepPublishing-TotalNetworkTools2002-Win95.iso / Archive / Misc Servers / Zope.exe / PYGRAM.PY < prev    next >
Encoding:
Text File  |  1999-11-03  |  32.1 KB  |  990 lines

  1. # rules for python
  2. # based on grammar given in Programming Python by Mark Lutz
  3.  
  4. # EDIT THIS: THE DIRECTORY IN WHICH TO MARSHAL THE
  5. # GRAMMAR DATA STRUCTURES.
  6. #
  7. ARCHIVE = "."  
  8.  
  9. marshalfilename = ARCHIVE + "/pygram.mar"
  10.  
  11. pyrules = """
  12.  
  13. all ::
  14.  
  15. ## input terminates with "fake" dedent (forces read of all file)
  16.  
  17. @R all1 :: all >> file_input DEDENT
  18.  
  19. ## 1 term newline
  20.  
  21. ##@R lead_blank :: file_input >> NEWLINE file_input
  22.  
  23. @R top_stmt :: file_input >> file_input stmt
  24. @R file_input :: file_input >> stmt
  25.  
  26.  
  27. ## 2
  28. @R simple :: stmt >> simple_stmt
  29. @R compound :: stmt >> compound_stmt
  30.  
  31. ## 3 punct ; term NEWLINE
  32. @R one_small :: simple_stmt >> small_stmt NEWLINE
  33. @R more_small :: simple_stmt >> small_stmt ; simple_stmt
  34. @R small_semi :: simple_stmt >> small_stmt ; NEWLINE
  35.  
  36. ## 4 kw pass
  37. @R smexpr :: small_stmt >> expr_stmt
  38. @R smassn :: small_stmt >> assn
  39. @R smprint :: small_stmt >> print_stmt
  40. @R smdel :: small_stmt >> del_stmt
  41. @R smpass :: small_stmt >> pass
  42. @R smflow :: small_stmt >> flow_stmt
  43. @R smimport :: small_stmt >> import_stmt
  44. @R smglobal :: small_stmt >> global_stmt
  45. ## access ignored
  46. @R smexec :: small_stmt >> exec_stmt
  47.  
  48. ## 5
  49. @R cmif :: compound_stmt >> if_stmt
  50. @R cmwhile :: compound_stmt >> while_stmt
  51. @R cmfor :: compound_stmt >> for_stmt
  52. @R cmtry :: compound_stmt >> try_stmt
  53. @R cmdef :: compound_stmt >> funcdef
  54. @R cmclass :: compound_stmt >> classdef
  55.  
  56. ##6
  57. @R exprlist :: expr_stmt >> testlist
  58. ##@R assignment :: expr_stmt >> assn
  59. @R assn1 :: assn >> testlist = testlist
  60.  
  61. @R assnn :: assn >> testlist = assn
  62.  
  63. @R assn1c :: assn >> testlist , = testlist
  64.  
  65. @R assn1c2 :: assn >> testlist , = testlist ,
  66.  
  67. @R assnnc :: assn >> testlist , = assn
  68.  
  69. ##testing @R exprassn :: expr_stmt >> expr_stmt = testlist 
  70.  
  71. @R exprlistc :: expr_stmt >> testlist ,
  72.  
  73. ##testing @R exprassnc :: expr_stmt >> expr_stmt = testlist ,
  74.  
  75. ##7 kw print
  76. @R rprint0 :: print_stmt >> print
  77. @R rprint :: print_stmt >> print testlist 
  78. @R rprintc :: print_stmt >> print testlist ,
  79.  
  80. ##8 kw del
  81. @R rdel :: del_stmt >> del exprlist
  82.  
  83. ##9 trivially handled in #4
  84.  
  85. ##10 kw raise continue break return
  86.  
  87. ## eliminates 11 12 13 14
  88. @R rbreak  :: flow_stmt >> break
  89. @R rcontinue :: flow_stmt >> continue
  90. @R rreturn0 :: flow_stmt >> return
  91. @R rreturn :: flow_stmt >> return testlist 
  92. @R rreturnc :: flow_stmt >> return testlist ,
  93. @R rraise1 :: flow_stmt >> raise test
  94. @R rraise2 :: flow_stmt >> raise test , test
  95. @R rraise3 :: flow_stmt >> raise test , test , test
  96.  
  97. ## 11 12 13 14 skipped
  98.  
  99. ## 15 kw import from
  100. @R rimport :: import_stmt >> import dotted_name_list 
  101. @R rimportc :: import_stmt >> import dotted_name_list ,
  102. @R dnlist1 :: dotted_name_list >> dotted_name
  103. @R dnlistn :: dotted_name_list >> dotted_name_list , dotted_name
  104. @R rfrom :: import_stmt >> from dotted_name import name_list 
  105. @R rfroms :: import_stmt >> from dotted_name import *
  106. @R rfromc :: import_stmt >> from dotted_name import name_list ,
  107. @R nlistn :: name_list >> name_list  , NAME
  108. @R nlist1 :: name_list >> NAME
  109.  
  110. ##16 nt NAME
  111. @R dn1 :: dotted_name >> NAME
  112. @R dnn :: dotted_name >> dotted_name . NAME
  113.  
  114. ##17 kw global
  115. @R global1 :: global_stmt >> global NAME 
  116. @R globaln :: global_stmt >> global_stmt , NAME 
  117.  
  118. ## 18 19 ignored
  119.  
  120. ##20 kw exec in
  121. @R exec1 :: exec_stmt >> exec expr
  122. @R exec2 :: exec_stmt >> exec expr in test
  123. @R exec3 :: exec_stmt >> exec expr in test , test
  124.  
  125. ##21  kw if elif else punct :
  126. @R ifr :: if_stmt >> if test : suite elifs
  127. @R elifs0 :: elifs >>
  128. @R relse :: elifs >> else : suite
  129. @R elifsn :: elifs >> elif test : suite elifs
  130.  
  131. ##22 kw while
  132. @R while1 :: while_stmt >> 
  133. while test : 
  134.     suite
  135. @R while2 :: while_stmt >> 
  136. while test : 
  137.    suite 
  138. else : 
  139.    suite
  140.  
  141. ##23 kw for
  142. @R for1 :: for_stmt >> 
  143. for exprlist in testlist  : 
  144.      suite
  145. @R for2 :: for_stmt >> 
  146. for exprlist in testlist  : 
  147.      suite 
  148. else : 
  149.      suite
  150.  
  151. ##24 kw try
  152. @R tryr :: try_stmt >> try : suite excepts
  153. @R excepts1 :: excepts >> except_clause : suite
  154. @R excepts2 :: excepts >> except_clause : suite else : suite
  155. @R exceptsn :: excepts >> except_clause : suite excepts
  156. @R tryf :: try_stmt >> try : suite finally : suite
  157.  
  158. ##25 kw except
  159. @R except0 :: except_clause >> except 
  160. @R except1 :: except_clause >> except test
  161. @R except2 :: except_clause >> except test , test
  162.  
  163. ##26
  164. @R class1 :: classdef  >> class NAME : suite
  165. @R class2 :: classdef  >> class NAME ( testlist ) : suite
  166.  
  167. ##27 kw def
  168. @R rdef :: funcdef >> def NAME parameters : suite
  169.  
  170. ##28, 29 punct = * 
  171.  
  172. ## (modified from grammar presented)
  173. @R params1 :: parameters >> ( varargslist )
  174. @R params1c :: parameters >> ( varargslist , )
  175. @R params2 :: varargslist >> 
  176.  
  177. ## this is way too permissive: fix at semantic level
  178. @R params3 :: varargslist >> arg
  179. @R params4 :: varargslist >> varargslist , arg
  180. @R argd :: arg >> NAME = test
  181. @R arg2 :: arg >> fpdef
  182. @R arg3 :: arg >> * NAME
  183. @R arg4 :: arg >> ** NAME
  184.  
  185. ## 30
  186. @R fpdef1 :: fpdef  >> NAME
  187. @R fpdef2 :: fpdef  >>  ( fplist )
  188. @R fpdef2c :: fpdef  >>  ( fplist , )
  189.  
  190. ##31
  191. @R fplist1 :: fplist >> fpdef
  192. @R fplistn :: fplist >> fplist , fpdef
  193.  
  194. ##32 t INDENT DEDENT
  195. @R ssuite :: suite >> simple_stmt
  196. @R csuite :: suite >> NEWLINE INDENT stmtseq DEDENT
  197. @R stmtseq1 :: stmtseq >> stmt
  198. @R stmtseqn :: stmtseq >> stmtseq stmt
  199.  
  200. ##33 kw or cancels 53
  201. @R testor :: test >> or_test
  202. @R testand :: or_test >> and_test
  203. @R testor1 :: or_test >> or_test or and_test
  204. ## @R testlambda0 :: test >> lambda : test REDUNDANT
  205. @R testlambda1 :: test >> lambda varargslist : test
  206.  
  207. ##34 kw and
  208. @R andnot :: and_test >> not_test
  209. @R andand :: and_test >> and_test and not_test
  210.  
  211. ##35 kw not
  212. @R notnot :: not_test >> not not_test
  213. @R notcmp :: not_test >> comparison
  214.  
  215. ##36 NOTE KWS == >= <= <> !=
  216. @R cmpexpr :: comparison >> expr
  217. @R cmplt :: comparison >> comparison < expr
  218. @R cmpgt :: comparison >> comparison > expr
  219. @R cmpeq :: comparison >> comparison == expr
  220. @R cmpge :: comparison >> comparison >= expr
  221. @R cmple :: comparison >> comparison <=  expr
  222. @R cmpnep :: comparison >> comparison <> expr
  223. @R cmpne :: comparison >> comparison != expr
  224. @R cmpin :: comparison >> comparison in expr
  225. @R cmpnotin :: comparison >> comparison not in expr
  226. @R cmpis :: comparison >> comparison is expr
  227. @R cmpisnot :: comparison >> comparison is not expr
  228.  
  229. ##37 kw is not punct > < ! (eliminated)
  230.  
  231. ##38 p |
  232. @R expr_xor :: expr >> xor_expr
  233. @R expr_lor :: expr >> expr | xor_expr
  234.  
  235. ##39 p ^
  236. @R xor_and :: xor_expr >> and_expr
  237. @R xor_xor :: xor_expr >> xor_expr ^ and_expr
  238.  
  239. ##40
  240. @R and_shift :: and_expr >> shift_expr
  241. @R and_and :: and_expr >> and_expr & shift_expr
  242.  
  243. ##41 note kw's << >x> note goofy x to avoid confusing the grammar
  244. @R shift_arith :: shift_expr >> arith_expr
  245. @R shift_left :: shift_expr >> shift_expr << arith_expr
  246. @R shift_right :: shift_expr >> shift_expr >x> arith_expr
  247.  
  248. ##42
  249. @R arith_term :: arith_expr >> term
  250. @R arith_plus :: arith_expr >> arith_expr + term
  251. @R arith_minus :: arith_expr >> arith_expr - term
  252.  
  253. ##43 p */%
  254. @R termfactor :: term >> factor
  255. @R termmul :: term >> term * factor
  256. @R termdiv :: term >> term / factor
  257. @R termmod :: term >> term % factor
  258.  
  259. ## stuff for power
  260. @R factorpower :: factor >> power
  261. @R factorexp :: factor >> factor ** power
  262.  
  263. ##44 p ~
  264. @R powera :: power >> atom trailerlist
  265. @R trailerlist0 :: trailerlist >> 
  266. @R trailerlistn :: trailerlist >> trailer trailerlist
  267. @R powerp :: power >> + power
  268. @R powerm :: power >> - power
  269. @R poweri :: power >> ~ power
  270.  
  271. ##45 t NUMBER STRING
  272. @R nulltup :: atom >> ( )
  273. @R parens :: atom >> ( testlist )
  274. @R parensc :: atom >> ( testlist , )
  275. @R nulllist :: atom >> [ ]
  276. @R list :: atom >> [ testlist  ]
  277. @R listc :: atom >> [ testlist , ]
  278. @R nulldict :: atom >> { }
  279. @R dict :: atom >> { dictmaker   }
  280. @R dictc :: atom >> { dictmaker , }
  281. @R repr :: atom >> ` testlist  `
  282. ## @R reprc :: atom >> ` testlist , ` doesn't work, apparently
  283. @R aname :: atom >> NAME
  284. ## note number to be broken out into FLOAT OCTINT HEXINT INT
  285. @R anumber :: atom >> NUMBER
  286. @R astring :: atom >> stringseq
  287. @R stringseq0 :: stringseq >> STRING
  288. @R stringseqn :: stringseq >> stringseq STRING
  289.  
  290. ##46 
  291. @R nullcall :: trailer >> ( )
  292. @R call :: trailer >> ( arglist  )
  293. @R callc :: trailer >> ( arglist , )
  294. @R index :: trailer >> [ subscriptdots ]
  295. @R getattr :: trailer >> . NAME
  296.  
  297. ##47
  298. @R arg1 :: arglist >> argument
  299. @R argn :: arglist >> arglist , argument
  300. ##@R argn1 :: arglist >> arglist , NAME = test
  301.  
  302. ##48 ( !!!! is this wrong in PP?)
  303.  
  304. @R posarg :: argument >> test
  305.  
  306. ## here the left test should be a NAME always, but parser doesn't like it
  307. @R namearg :: argument >> test = test
  308.  
  309. ##49 this IS wrong in PP (numeric ext)
  310. @R nodots :: subscriptdots >> subscriptseq
  311. @R yesdots :: subscriptdots >> subscriptseq , . . . , subscriptseq
  312. @R subscript1 :: subscriptseq >> subscript
  313. @R subscriptn :: subscriptseq >> subscriptseq , subscript
  314. @R subscriptt :: subscript >> test
  315. @R subscripts0 :: subscript >> :
  316. @R subscriptsL :: subscript >> test :
  317. @R subscriptsR :: subscript >> : test
  318. @R subscripts :: subscript >> test : test
  319.  
  320. ##50
  321. @R exprlist1 :: exprlist >> expr
  322. @R exprlistn :: exprlist >> exprlist , expr
  323.  
  324. ##51
  325. @R testlist0 :: testlist >> test
  326. @R testlistn :: testlist >> testlist , test
  327.  
  328. ##52
  329. @R dictmaker1 :: dictmaker >> test : test
  330. @R dictmaker2 :: dictmaker >> dictmaker , test : test
  331.  
  332. """
  333.  
  334. nonterms = """
  335. subscriptdots subscript arg
  336. argument arglist subscriptseq params trailerlist
  337. factor atom trailer dictmaker stringseq power
  338. xor_expr and_expr shift_expr arith_expr term
  339. and_test or_test not_test comparison comp_op expr
  340. fplist stmtseq varargslist assn
  341. expr elifs suite excepts parameters pbasic pdefault pspecial
  342. testlist exprlist test dotted_name_list dotted_name name_list
  343. if_stmt while_stmt for_stmt try_stmt funcdef classdef
  344. expr_stmt print_stmt del_stmt flow_stmt import_stmt global_stmt
  345. small_stmt compound_stmt stmt simple_stmt exec_stmt
  346. file_input except_clause fpdef cmp_op
  347. all
  348. """
  349.  
  350. import string
  351. # python needs special handling for the lexical stuff
  352. NAMEre = "[" + string.letters + "_][" + string.letters+string.digits +"]*"
  353. NUMBERre = "[" + string.digits + "]+" # temporary!
  354. STRINGre = '"[^"\n]*"' # to be overridden in lexdict
  355. #NEWLINEre = "\n" # to be overridden in lexdict
  356. INDENTre = "#" # a fake! to be overridden
  357. DEDENTre = "#" # a fake! to be overridden
  358.  
  359. def echo(str):
  360.     return str
  361.  
  362. def DeclareTerminals(Grammar):
  363.     Grammar.Addterm("NAME", NAMEre, echo)
  364.     Grammar.Addterm("NUMBER", NUMBERre, echo)
  365.     Grammar.Addterm("STRING", STRINGre, echo)
  366.     #Grammar.Addterm("NEWLINE", NEWLINEre, echo) # newline is kw!
  367.     Grammar.Addterm("INDENT", INDENTre, echo)
  368.     Grammar.Addterm("DEDENT", DEDENTre, echo)
  369.  
  370. # not >x> is a fake!
  371. keywords = """
  372. and break class continue def del elif else except exec
  373. finally for from global if import in is lambda not or pass
  374. print raise return try while == >= <= <> != >x> << NEWLINE
  375. **
  376. """
  377.  
  378. import kjParser, string, regex
  379. from kjParser import KEYFLAG, ENDOFFILETERM
  380.  
  381. alphanumunder = string.letters+string.digits+"_"
  382. alpha = string.letters + "_"
  383.  
  384. # components that are part of a identifier (cannot be next to kw).
  385. id_letters = map(None, alphanumunder)
  386.  
  387. # terminator re for names
  388. nametermre = "[^" + alphanumunder + "]"
  389. nameterm = regex.compile(nametermre)
  390.  
  391. # terminator re for numbers (same as above but allow "." in num).
  392. numtermre =  "[^" + alphanumunder + "\.]"
  393. numterm = regex.compile(numtermre)
  394.  
  395. parseerror = "parseerror"
  396.  
  397. pycommentre = "\(#.*\)"
  398.  
  399. # whitespace regex outside of brackets
  400. #  white followed by (comment\n maybe repeated)
  401. #  DON'T EAT NEWLINE!!
  402. pywhiteoutre = "\([ \t\r\014]\|\\\\\n\)*%s?" % pycommentre
  403. pywhiteout = regex.compile(pywhiteoutre)
  404.  
  405. # whitespace regex inside brackets
  406. #  white or newline possibly followed by comment, all maybe repeated
  407. pywhiteinre = pywhiteoutre #"[ \t\r]*\(\\\\\n\)*%s?" % pycommentre
  408. pywhitein = regex.compile(pywhiteinre)
  409.  
  410. # totally blank lines (only recognize if next char is newline)
  411. #allblankre = "\n" + pywhiteinre
  412. #allblank = regex.compile(allblankre)
  413.  
  414. # re for indentation (might accept empty string)
  415. indentp = regex.compile("[\t ]*")
  416.  
  417. # two char kws and puncts
  418. char2kw = ["if", "or", "in", "is"]
  419. punct2 = ["<>", "<<", ">>", "<=", ">=", "!=", "**", "=="]
  420.  
  421. # >two char kws as map of first 3 chars to others
  422. char3k_data = """
  423.   and break class continue def del elif else except
  424.   finally for from global import lambda not pass print
  425.   raise return try while exec
  426. """
  427.  
  428. char3kw = string.split(char3k_data)
  429. char3kwdict = {}
  430. for x in char3kw:
  431.     char3kwdict[x[:3]] = x
  432.  
  433. # NOTE: newline is treated same as a punctuation
  434. # NOTE: "' ARE NOT PUNCTS
  435. punct = "~!#%^&*()-+=|{}<>,.;:/[]{}\n`"
  436. punctlist = map(None, punct)
  437.  
  438. kwmap = {}
  439. for x in char2kw + punct2 + char3kw + map(None, punct):
  440.     # everything parses as length 1 to the outer world.
  441.     kwmap[x] = (((KEYFLAG, x), x), 1)
  442.  
  443. # special hack
  444. kwmap[">>"] = (((KEYFLAG, ">x>"), ">x>"), 1)
  445. newlineresult = kwmap["\n"] = (((KEYFLAG, "NEWLINE"), "NEWLINE"), 1)
  446.  
  447. #finaldedent = (((TERMFLAG, "DEDENT"), ""), 1)
  448.  
  449. # Python lexical dictionary.
  450.  
  451. ### MUST HANDLE WHOLELY BLANK LINES CORRECTLY!
  452.  
  453. class pylexdict(kjParser.LexDictionary):
  454.    def __init__(self):
  455.        kjParser.LexDictionary.__init__(self)
  456.        # need to add special map for >>
  457.        self.brackets = 0 # count of active brackets
  458.        self.realindex = 0 # where to start
  459.        self.indents = [""] # stack of indents (start with a fake one)
  460.        self.lineno = 0
  461.        self.atdedent = 0
  462.        ### handle multiple dedents correctly!!!
  463.        ### translate tabs to 8 spaces...
  464.        from kjParser import TERMFLAG
  465.        self.NAMEflag = (TERMFLAG, "NAME")
  466.        self.STRINGflag = (TERMFLAG, "STRING")
  467.        self.NEWLINEflag = (TERMFLAG, "NEWLINE")
  468.        self.INDENTflag = (TERMFLAG, "INDENT")
  469.        self.DEDENTflag = (TERMFLAG, "DEDENT")
  470.        self.NUMBERflag = (TERMFLAG, "NUMBER")
  471.  
  472.    def endoffile(self, String):
  473.        # pop off all indentations!
  474.        indents = self.indents
  475.        #lastresult = self.lastresult
  476.        self.realindex = len(String)
  477.        if not indents:
  478.           # pop indents
  479.           #print "eof after dedent"
  480.           result = self.lastresult = (ENDOFFILETERM, 0)
  481.        else:
  482.           #print "eof as dedent after", self.lastresult
  483.           del indents[-1]
  484.           if indents:
  485.              dedent = indents[-1]
  486.           else:
  487.              dedent = ""
  488.           result = self.lastresult = ((self.DEDENTflag, dedent), 1)
  489.        #print "returning eof", result, "after", lastresult
  490.        return result
  491.  
  492.    def Token(self, String, StartPosition):
  493.        #print "Token", (StartPosition, 
  494.        #  `String[self.realindex:self.realindex+20]`, self.lastresult)
  495.        # HAVE TO FAKE OUT LEXER FOR DEDENTS
  496.        # STARTPOSITION COUNTS # OF TOKEN, NOT STRING POSITION
  497.        # STRING POSITION IS MAINTAINED IN LexDict object.
  498.        lastindex = self.lastindex
  499.        lastresult = self.lastresult
  500.        if self.laststring is not String:
  501.           #print "parsing new string"
  502.           self.laststring = String
  503.           # special hack: skip lead whitespace
  504.           cursor = 0
  505.           self.lineno = 1
  506.           while 1:
  507.              test = pywhitein.match(String, cursor)
  508.              if test<0: break
  509.              next = cursor + test
  510.              #print "lead skip:", next, String[cursor:next]
  511.              if String[next]!="\n": break
  512.              #skipped = String[cursor:next]
  513.              #if "\n" in skipped:
  514.              #   self.lineno = (
  515.              #    self.lineno + len(string.splitfields(skipped, "\n")))
  516.              #self.lineno = self.lineno+1
  517.              cursor = next + 1
  518.           self.realindex = cursor
  519.           self.saveindex = 0
  520.           self.indents = [""] # stack of indents (start with a fake one)
  521.           # pretend we saw a newline
  522.           self.lastresult = newlineresult
  523.           if StartPosition!=0:
  524.              self.laststring = None
  525.              raise ValueError, "python lexical parsing must start at zero"
  526.           lastindex = self.lastindex
  527.           lastresult = None
  528.        elif lastindex == StartPosition:
  529.           #print "returning lastresult ", lastresult
  530.           return lastresult
  531.        elif lastindex != StartPosition-1:
  532.           raise ValueError, "python lexer can't skip tokens"
  533.  
  534.        #print "parsing", StartPosition, lastresult
  535.        # do newline counting here!
  536.        delta = String[self.saveindex: self.realindex]
  537.        #print "delta", `delta`
  538.        if "\n" in delta:
  539.           #print self.lineno, self.saveindex, self.realindex, `delta`
  540.           self.lineno = self.lineno + len(
  541.             string.splitfields(delta, "\n")) - 1
  542.        realindex = self.saveindex = self.realindex
  543.        self.lastindex = StartPosition
  544.  
  545.        # skip whitespace (including comments)
  546.        ### needs to be improved to parse blank lines, count line numbers...
  547.        # skip all totally blank lines (don't eat last newline)
  548.        atlineend = (String[realindex:realindex+1] == "\n"
  549.                     or lastresult is newlineresult
  550.                     or self.atdedent)
  551.        skipnewlines = (lastresult is newlineresult or
  552.                        self.atdedent or
  553.                        self.brackets>0)
  554.        if atlineend: #String[realindex:realindex+1]=="\n":
  555.           #print "trying to skip blank lines", String[realindex:realindex+10]
  556.           while 1:
  557.              #if String[realindex:realindex+1]=="\n":
  558.              #   start = realindex+1 # move past current newline
  559.              #   self.lineno = self.lineno + 1
  560.              #else:
  561.              #   start = realindex
  562.              start = realindex
  563.              if skipnewlines:
  564.                 while String[start:start+1]=="\n":
  565.                    start = start+1
  566.                    #self.lineno = self.lineno+1
  567.              #print "matching", `String[start:start+10]`
  568.              skip = pywhitein.match(String, start)
  569.              #print "skip=", skip
  570.              if skip<0: break
  571.              rs = skip + realindex + (start-realindex)
  572.              if rs==realindex: break
  573.              #print "at", rs, `String[rs]`
  574.              if (rs<len(String) and 
  575.                  (String[rs] == "\n" or 
  576.                   (skipnewlines and String[rs-1:rs]=="\n"))):
  577.                 #print "skipping blank line"
  578.                 #if lastresult is newlineresult or self.brackets>0: 
  579.                 #   rs = rs + 1
  580.                 #skipped = String[start:rs]
  581.                 #if "\n" in skipped:
  582.                    #self.lineno = self.lineno + len(
  583.                    #   string.splitfields(skipped, "\n"))
  584.                 self.realindex = realindex = rs
  585.                 #self.lineno = self.lineno+1
  586.              else:
  587.                 if skipnewlines: self.realindex = realindex = start
  588.                 break
  589.        #print "after skipping blank lines", `String[realindex:realindex+20]`
  590.        skipto = realindex
  591.        skip = 0
  592.        if self.brackets>0:
  593.           while 1:
  594.              #print "skipping white in brackets", skipto
  595.              if realindex>len(String):
  596.                 break
  597.              if String[skipto]=="\n":
  598.                 #self.lineno = self.lineno+1
  599.                 skipto = skipto + 1
  600.                 self.realindex = realindex = skipto
  601.                 continue
  602.              skip = pywhiteout.match(String, skipto)
  603.              nextskipto = skipto+skip
  604.              #skipped = String[skipto:nextskipto]
  605.              #if "\n" in skipped:
  606.              #   self.lineno = self.lineno+len(
  607.              #       string.splitfields(skipped, "\n"))
  608.              if skip>0:
  609.                 skipto = nextskipto
  610.              else: break
  611.           skip = skipto - realindex
  612.        elif not atlineend:
  613.           skip = pywhitein.match(String, realindex)
  614.        if skip<=0: 
  615.           skip = 0
  616.        else:
  617.           #print "skipping", skip
  618.           nextri = realindex + skip
  619.           #skipped = String[realindex:nextri]
  620.           #if "\n" in skipped:
  621.           #   self.lineno = self.lineno + len(
  622.           #    string.splitfields(skipped, "\n"))
  623.           realindex = self.realindex = nextri
  624.        if realindex>=len(String):
  625.           return self.endoffile(String)
  626.        # now look for a keyword, name, number, punctuation, 
  627.        # INDENT, DEDENT, NEWLINE
  628.        first = String[realindex]
  629.        #if last parse was newline and not in brackets:
  630.        #   look for indent/dedent
  631.        if (self.brackets<=0 and (lastresult is newlineresult or self.atdedent)
  632.            and first != "\n"):
  633.           #print "looking for dent", realindex, `String[realindex:realindex+20]`
  634.           match = indentp.match(String, realindex)
  635.           if match>=0:
  636.              dent = String[realindex: realindex+match]
  637.              #print "dent match", match, `dent`
  638.              oldindex = realindex
  639.              self.realindex = realindex = realindex+match
  640.              # replace tabs with 8 spaces
  641.              dent = string.joinfields(string.splitfields(dent, "\t"),
  642.                                       "        ")
  643.              dents = self.indents
  644.              lastdent = dents[-1]
  645.              ldl = len(lastdent)
  646.              dl = len(dent)
  647.              #print "last", ldl, dents
  648.              if ldl<dl:
  649.                 self.atdedent = 0
  650.                 result = self.lastresult = ((self.INDENTflag, dent), 1)
  651.                 dents.append(dent)
  652.                 #print "indent ", result, dents
  653.                 return result
  654.              if ldl>dl:
  655.                 self.realindex = oldindex # back up, may have to see it again!
  656.                 self.atdedent = 1
  657.                 result = self.lastresult = ((self.DEDENTflag, dent), 1)
  658.                 del dents[-1]
  659.                 #print "dedent ", result, dl, dents
  660.                 return result
  661.              # otherwise, indentation is same, keep looking
  662.              # might be at eof now:
  663.              if realindex>=len(String):
  664.                 #print "returning eof"
  665.                 return self.endoffile(String)
  666.              first = String[realindex]
  667.        self.atdedent = 0
  668.        from string import digits #, letters
  669.        if (first in punctlist and
  670.            # special case for .123 numbers (yuck!)
  671.            (first!="." or String[realindex+1] not in digits)):
  672.           # is it a 2 char punct?
  673.           first2 = String[realindex:realindex+2]
  674.           if first2 in punct2:
  675.              result = self.lastresult = kwmap[first2]
  676.              self.realindex = realindex+2
  677.              #print "2 digit punct", result
  678.              return result
  679.           # otherwise, just return normal punct
  680.           result = self.lastresult = kwmap[first]
  681.           self.realindex = self.realindex + 1
  682.           ### special bookkeeping
  683.           if first=="\n":
  684.              result = newlineresult
  685.              #print "newline!"
  686.              #self.lineno = self.lineno+1
  687.           elif first in "[{(":
  688.              #print "bracket!"
  689.              self.brackets = self.brackets + 1
  690.           elif first in "]})":
  691.              #print "close bracket!"
  692.              self.brackets = self.brackets - 1
  693.           #print "1 digit punct", result
  694.           return result
  695.        if first in digits or first==".":
  696.           # parse a number...
  697.           skip = numterm.search(String, realindex)
  698.           if skip<=realindex:
  699.              raise parseerror, "number length<1 (!)"
  700.           thenumber = String[realindex:skip]
  701.           self.realindex = skip
  702.           ### note don't interpret number here!!
  703.           result = self.lastresult = ((self.NUMBERflag, thenumber), 1)
  704.           #print "number", result
  705.           return result
  706.        if first in alpha:
  707.           # try keyword...
  708.           first2 = String[realindex: realindex+2]
  709.           if first2 in char2kw:
  710.              if String[realindex+2:realindex+3] not in id_letters:
  711.                 # parse a 2 char kw first2
  712.                 result = self.lastresult = kwmap[first2]
  713.                 self.realindex = self.realindex+2
  714.                 #print "keyword 2", result
  715.                 return result
  716.           first3 = String[realindex: realindex+3]
  717.           if char3kwdict.has_key(first3):
  718.              the_kw = char3kwdict[first3]
  719.              the_end = realindex+len(the_kw)
  720.              if ((the_end<len(String)) and 
  721.                  (String[the_end] not in id_letters) and
  722.                  (String[realindex:the_end]==the_kw)):
  723.                 # parse the_kw
  724.                 self.realindex = the_end
  725.                 result = self.lastresult = kwmap[the_kw]
  726.                 #print "keyword +", result
  727.                 return result
  728.           #otherwise parse an identifier
  729.           #print "looking for name:", `String[realindex:realindex+10]`
  730.           skip = nameterm.search(String, realindex)
  731.           if skip<=realindex:
  732.              raise parseerror, "identifier length<1 (!)"
  733.           theid = String[realindex:skip]
  734.           self.realindex = skip
  735.           ### note don't interpret number here!!
  736.           result = self.lastresult = ((self.NAMEflag, theid), 1)
  737.           #print "id", result
  738.           return result
  739.        if first in "\"'":
  740.           # check for triplequotes
  741.           first3 = first*3
  742.           if String[realindex: realindex+3] == first3:
  743.              # parse triple quotes
  744.              start = place = realindex+3
  745.              while 1:
  746.                 last = string.find(String, first3, place)
  747.                 if last<0:
  748.                    raise parseerror, "failed to terminate triple quotes"
  749.                 if String[last-1:last]=="\\" and String[last-2:last-1]!="\\":
  750.                    place = last+1
  751.                 else: break
  752.              the_string = String[start: last]
  753.              self.realindex = last+3
  754.              result = self.lastresult = ((self.STRINGflag, the_string), 1)
  755.              #print "3q string", result
  756.              # count the newlines!
  757.              #newlinecount = len(string.splitfields(the_string, "\n"))
  758.              #self.lineno = self.lineno+newlinecount
  759.              #print "triple quotes", result
  760.              return result
  761.           else:
  762.              # parse single quotes
  763.              sanity = start = place = realindex+1
  764.              done = 0
  765.              while 1:
  766.                 sanity = min(string.find(String, "\n", sanity), len(String))
  767.                 if sanity<start: 
  768.                    sanity=len(String)
  769.                    break
  770.                 if String[sanity-1]!="\\":
  771.                    break
  772.                 else:
  773.                    #self.lineno = self.lineno+1
  774.                    sanity = sanity + 1
  775.              while 1:
  776.                 last = string.find(String, first, place)
  777.                 if last<0 or last>sanity:
  778.                    raise parseerror, "failed to terminate single quotes"
  779.                 if String[last-1:last]=="\\":
  780.                    # are we at the end of an odd number of backslashes? (yuck!)
  781.                    bplace = last-1
  782.                    while String[bplace:bplace+1]=="\\":
  783.                       bplace = bplace-1
  784.                    if (last-bplace)%2==1: 
  785.                       break # the end quote is real!
  786.                    place = last+1
  787.                 else: break
  788.              the_string = String[start:last]
  789.              self.realindex = last+1
  790.              result = self.lastresult = ((self.STRINGflag, the_string), 1)
  791.              #print "1q string", result
  792.              return result
  793.        #print (String[realindex-20:realindex-1], String[realindex],
  794.        #       String[realindex+1:realindex+20])
  795.        raise parseerror, "invalid first: " + `first`
  796.  
  797. # use a modified lexstringwalker
  798. class pylexstringwalker(kjParser.LexStringWalker):
  799.    def DUMP(self):
  800.        kjParser.DumpStringWindow(self.String, self.LexDict.realindex)
  801.  
  802. ## a HORRIBLE HACK! of a hack: override the DoParse of Grammar
  803. ## to give Python line numbers.  RELIES ON GLOBAL pyg
  804. ##
  805. def hackDoParse(String, Context=None, DoReductions=1):
  806.     import sys, kjParser
  807.     try:
  808.         # construct the ParserObj
  809.         # add a newline to front to avoid problem with leading comment
  810.         #String = "\n%s\n" % String
  811.         Stream = pylexstringwalker( String, pyg.LexD )
  812.         Stack = [] # {-1:0} #Walkers.SimpleStack()
  813.         ParseOb = kjParser.ParserObj( pyg.RuleL, Stream, pyg.DFA, Stack, \
  814.                          DoReductions, Context )
  815.         # do the parse
  816.         ParseResult = ParseOb.GO()
  817.         # return final result of reduction and the context
  818.         return (ParseResult[1], Context)
  819.         #return kjParser.Grammar.DoParse(pyg, String, Context, DoReductions)
  820.     except: ### for testing!!
  821.         t, v = sys.exc_type, sys.exc_value
  822.         v = ("near line", pyg.LexD.lineno, v)
  823.         raise t, v
  824.  
  825. buildinfo = """
  826. Please edit the ARCHIVE parameter of this module (%s)
  827. to place the python grammar archive in a standard
  828. directory to prevent the module from rebuilding
  829. the python grammar over and over and over...
  830. """ % __name__
  831.  
  832. def GrammarBuild():
  833.     global pyg
  834.     import kjParseBuild
  835.     pyg = kjParseBuild.NullCGrammar()
  836.     pyg.DoParse = hackDoParse
  837.     # override lexical dict here
  838.     pyg.LexD = pylexdict()
  839.     DeclareTerminals(pyg)
  840.     pyg.Keywords(keywords)
  841.     pyg.punct("~!#%^&*()-+=|{}'`<>,.;:/[]{}")
  842.     pyg.Nonterms(nonterms)
  843.     pyg.Declarerules(pyrules)
  844.     print buildinfo
  845.     print "compiling... this may take a while..."
  846.     pyg.Compile()
  847.     print "dumping"
  848.     outfile = open(marshalfilename, "wb")
  849.     pyg.MarshalDump(outfile)
  850.     outfile.close()
  851.     print "self testing the grammar"
  852.     test(pyg)
  853.     print "\n\ndone with regeneration"
  854.     return pyg
  855.  
  856. def unMarshalpygram():
  857.     global pyg
  858.     import kjParser
  859.     print "loading"
  860.     try:
  861.        infile = open(marshalfilename, "rb")
  862.     except IOError:
  863.        print marshalfilename, "not found, attempting creation"
  864.        pyg = GrammarBuild()
  865.     else:
  866.        pyg = kjParser.UnMarshalGram(infile)
  867.        infile.close()
  868.     pyg.DoParse = hackDoParse
  869.     # lexical override
  870.     pyg.LexD = pylexdict()
  871.     DeclareTerminals(pyg)
  872.     # BindRules(pyg)
  873.     if dotest: 
  874.        print "self testing the grammar"
  875.        test(pyg)
  876.     return pyg
  877.  
  878.  
  879. # not used, commented
  880. #### interpretation rules/classes
  881. #
  882. #def zeroth(list, Context):
  883. #    return list[0] # eg, for all1, ignore all but first
  884. #
  885. ## file_input, stmt, simple_stmt, compound_stmt give list of statement_ob
  886. #def append(list, Context):
  887. #    "eg, for top_stmt, conjoin two smt lists"
  888. #    return list[0] + list[1]
  889. #
  890. ## file_input >zeroth
  891. #
  892. ## simple, compound, one_small, small_semi: echol
  893. #def echol(list, Context):
  894. #    return list
  895. #
  896. ## more_small > seq_sep
  897. #def seq_sep(list, Context):
  898. #    list[0].append(list[2])
  899. #    return list[0]
  900. #
  901. ## smexpr, smassn, smpring, smdel, smflow, smimport, smglobal, smexec
  902. ##  > zeroth
  903. #
  904. ## cmif, cmwhile, cmfor, cmtry, cmdef, cmclass > zeroth
  905. #
  906. #
  907. #def BindRules(pyg):
  908. #    for name in string.split("""
  909. #        all1 file_input cmif cmwhile cmfor cmtry cmdef cmclass
  910. #        smexpr smassn smprint smdel smflow smimport smglobal smexec
  911. #        """):
  912. #        pyg.Bind(name, zeroth)
  913. #    for name in string.split("""
  914. #        simple compound one_small small_semi
  915. #        """):
  916. #        pyg.Bind(name, echol)
  917. #    pyg.Bind("top_stmt", append)
  918. #    pyg.Bind("more_small", seq_sep)
  919.  
  920. teststring = """#
  921. #
  922. # a test string
  923. #
  924. from string import join, split
  925. '''
  926. import regex
  927.  
  928. for a in l:
  929.     a.attr, a[x], b = c
  930. else:
  931.     d = b
  932. '''
  933. class zzz:
  934.    ''' 
  935.    #doc string 
  936.    '''
  937.    '''
  938.    global regex, join
  939.    
  940.    d = {} 
  941.    for i in range(10): d[i] = i
  942.    '''
  943.    def test(c,s):
  944.        return "this" 
  945.        while not done:
  946.              print done
  947.              break
  948.        list = [1,2,3]
  949.          # comment
  950.        return 5
  951.    
  952.    
  953.    n,x = 89 >> 90 + 6 / 7 % x + z << 6 + 2 ** 8
  954.  
  955. if x==5:
  956.    while y:
  957.      for i in range(6):
  958.          raise SystemError, "oops"
  959.  
  960.  
  961. """
  962.  
  963. #teststring ="""\
  964. ## comment
  965. #if x in y: print z
  966. #elif 1: print w
  967. #"""
  968.  
  969. '''
  970. teststring="""
  971. exec "print 1"
  972. """
  973. '''
  974.  
  975. def test(grammar, context=None, teststring=teststring):
  976.        from time import time
  977.        now = time()
  978.        x = grammar.DoParse1(teststring, context)
  979.        elapsed = time()-now
  980.        print x
  981.        print elapsed
  982.        return x
  983.    
  984. regen = 0
  985. dotest = 0
  986.    
  987. if __name__ == "__main__" : 
  988.       if regen: GrammarBuild()
  989.       unMarshalpygram()
  990.